import pianoPlayerEvent from '@/event/pianoPlayerEvent'
import { MusicPlayerSong } from '@/types/music'
import { getMusicKeyByNote, playerOnKey } from './data'
import { sleep } from '@/utils/common'

const playerRef = {
  value: null as MusicPlayerSong | null,
  activeController: null as AutoPlaySongController | null,
}

pianoPlayerEvent.on('loadSong', (ref) => {
  ref.value = playerRef.value
  if (playerRef.activeController) {
    ref.currentTime = playerRef.activeController.currentTime
  }
})
pianoPlayerEvent.on('songPause', (song) => {
  if (song.name == playerRef.value?.name) {
    playerRef.activeController?.pause()
  }
})
pianoPlayerEvent.on('songReplay', (song, currentTime) => {
  if (song.name == playerRef.value?.name) {
    if (playerRef.activeController == null) {
      console.warn('songReplay activeController is null')
      return
    }
    playerRef.activeController.currentTime = currentTime
    playerRef.activeController.replay()
  }
})

export interface PianoRef {
  playerSong: (
    musicSong: MusicPlayerSong,
    onend?: () => any
  ) => AutoPlaySongController
}
/**
 * 自动演奏控制器
 */
export class AutoPlaySongController {
  private onEndHandler?: () => any
  private musicSong: MusicPlayerSong
  currentTime: number = 0
  private _isPause: boolean = false
  private playerVersion = 0
  constructor(musicSong: MusicPlayerSong) {
    this.musicSong = musicSong
  }
  get isPause() {
    return this._isPause
  }
  get allTime() {
    return this.musicSong.playerTime
  }
  onEnd(onEndHandler: () => any): void {
    this.onEndHandler = onEndHandler
  }
  pause(): void
  pause() {
    this._isPause = true
  }

  play(): void
  play() {
    const startTime = Date.now()
    const playerVersion = ++this.playerVersion
    this.playTask(playerVersion, startTime)
  }
  replay(): void
  async replay() {
    if (this.isPause) {
      this._isPause = false
    }
    const startTime = Date.now() - this.currentTime
    const playerVersion = ++this.playerVersion
    this.playTask(playerVersion, startTime)
  }
  playTask(playerVersion: number, startTime: number) {
    playerRef.value = this.musicSong
    const keys = this.musicSong.data
    let tmpKeyList = [...keys].filter((item) => item.time >= this.currentTime)
    playerRef.activeController = this
    const task = async () => {
      while (tmpKeyList.length > 0) {
        if (this.isPause || playerVersion !== this.playerVersion) {
          return
        }
        this.currentTime = Date.now() - startTime
        tmpKeyList = tmpKeyList.filter((item) => {
          if (item.time - this.currentTime < 400) {
            setTimeout(async () => {
              if (this.isPause) return
              const key = getMusicKeyByNote(item.note)!
              pianoPlayerEvent.emit('onKeyActive', key, true)
              playerOnKey(key)
              await sleep(300)
              pianoPlayerEvent.emit('onKeyActive', key, false)
            }, item.time - this.currentTime)
            return false
          }
          return true
        })
        pianoPlayerEvent.emit('playSongTimeUpdate', this.currentTime)
        await sleep(100)
      }
      while (this.currentTime < this.musicSong.playerTime) {
        if (this.isPause || playerVersion !== this.playerVersion) {
          return
        }
        this.currentTime = Date.now() - startTime
        pianoPlayerEvent.emit('playSongTimeUpdate', this.currentTime)
        await sleep(200)
      }
      await sleep(300)
      this.onEndHandler && this.onEndHandler()
      if (playerRef.activeController == this) {
        playerRef.activeController = null
      }
      pianoPlayerEvent.emit('onPlayerSongStop', this.musicSong)
      if (playerRef.value == this.musicSong) {
        playerRef.value = null
      }
    }
    task()
  }
  getProgress(): number
  getProgress() {
    return Math.min(1, this.currentTime / this.musicSong.playerTime)
  }
  stop(): void
  stop() {
    this._isPause = false
    this.playerVersion++
    if (this.onEndHandler) {
      this.onEndHandler()
      if (playerRef.activeController == this) {
        playerRef.activeController = null
      }
    }
    pianoPlayerEvent.emit('onPlayerSongStop', this.musicSong)
    if (playerRef.value == this.musicSong) {
      playerRef.value = null
    }
  }
}
